﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using DotNetUtilityLibrary;

namespace CryptographyDemonstration
{
	public partial class MainForm : Form
	{
		#region Properties

		private SymmetricAlgorithm mSelectedAlgorithm
		{
			get
			{
				return ((GenericWrapper<SymmetricAlgorithm>)
					cbAlgorithm.SelectedItem).Item;
			}
		}

		#endregion Properties

		#region Fields
		List<GenericWrapper<SymmetricAlgorithm> > mSymmetricAlgorithms =
			new List<GenericWrapper<SymmetricAlgorithm>>() {
				new GenericWrapper<SymmetricAlgorithm>(new AesCryptoServiceProvider(),
					TypeHelper.GetTypeFriendlyName),
				new GenericWrapper<SymmetricAlgorithm>(new DESCryptoServiceProvider(),
					TypeHelper.GetTypeFriendlyName),
				new GenericWrapper<SymmetricAlgorithm>(new RC2CryptoServiceProvider(),
					TypeHelper.GetTypeFriendlyName),
				new GenericWrapper<SymmetricAlgorithm>(new TripleDESCryptoServiceProvider(),
					TypeHelper.GetTypeFriendlyName),
				new GenericWrapper<SymmetricAlgorithm>(new RijndaelManaged(),
					TypeHelper.GetTypeFriendlyName),
				new GenericWrapper<SymmetricAlgorithm>(new AesManaged(),
					TypeHelper.GetTypeFriendlyName)
			};

		#endregion Fields

		#region Constructor

		public MainForm()
		{
			InitializeComponent();
			cbAlgorithm.DataSource = mSymmetricAlgorithms;
			cbAlgorithm.DisplayMember = "DisplayName";

			ValidateKeyAndIV();
		}

		#endregion Constructor

		#region Event Handlers

		private void btnSenderGenerateMetadata_Click(object sender, EventArgs e)
		{
			int keySize = mSelectedAlgorithm.LegalKeySizes[0].MaxSize / 8;
			byte[] keyBytes = new byte[keySize];
			int ivSize = mSelectedAlgorithm.BlockSize / 8;
			byte[] ivBytes = new byte[ivSize];
			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			rng.GetNonZeroBytes(keyBytes);
			rng.GetNonZeroBytes(ivBytes);
			txtIV.Text = ConvertHelper.BytesToHexString(ivBytes);
			txtKey.Text = ConvertHelper.BytesToHexString(keyBytes);
		}

		private void txtSenderKey_TextChanged(object sender, EventArgs e)
		{
			ValidateKeyAndIV();
		}

		private void txtSenderIV_TextChanged(object sender, EventArgs e)
		{
			ValidateKeyAndIV();
		}

		private void txtSenderText_TextChanged(object sender, EventArgs e)
		{
			if (ValidateKeyAndIV() == false)
			{
				MessageBox.Show(
					"Error: Please specify appropriate keys and initialization vectors.",
					"Cryptographic Error",
					MessageBoxButtons.OK,
					MessageBoxIcon.Error);
			}
		}

		private void cbSenderCryptographyAlgorithm_SelectedIndexChanged(object sender, EventArgs e)
		{
			ValidateKeyAndIV();
		}

		#endregion Event Handlers

		#region Private Methods

		private void Encrypt()
		{
			// Wrap up a change the CryptographyHelper with ConvertHelper calls to
			// transparently change the displaying functionality
			mSelectedAlgorithm.Key = ConvertHelper.HexStringToBytes(txtKey.Text);
			mSelectedAlgorithm.IV = ConvertHelper.HexStringToBytes(txtIV.Text);

			// Wrap up a change the CryptographyHelper with ConvertHelper calls to
			// transparently change the displaying functionality
			txtEncryptedText.Text =
				ConvertHelper.BytesToHexString(
					ConvertHelper.StringToBytes(
						CryptographyHelper.SymmetricEncrypt(
							mSelectedAlgorithm, txtPlainText.Text
						)
					)
				);
		}

		private bool ValidateKeyAndIV()
		{
			bool validKey = ValidateKey();
			bool validIV = ValidateIV();
			if (validKey && validIV)
			{
				txtPlainText.Enabled = true;
				txtEncryptedText.Enabled = true;
				try
				{
					Encrypt();
				}
				catch (CryptographicException ex)
				{
					txtPlainText.Enabled = false;
					txtEncryptedText.Enabled = false;
					txtEncryptedText.Text = "";
					txtKey.BackColor = Color.Pink;
					tlpSenderMetadata.SetColumnSpan(txtKey, 1);
					mErrorProvider.SetError(txtKey, ex.Message);
					MessageBox.Show(
						ex.Message,
						"Cryptographic Exception Encountered",
						MessageBoxButtons.OK,
						MessageBoxIcon.Error);
				}
				return true;
			}
			else
			{
				txtPlainText.Enabled = false;
				txtEncryptedText.Enabled = false;
				txtEncryptedText.Text = "";
				return false;
			}
		}

		private bool ValidateKey()
		{
			try
			{
				int keylength = ConvertHelper.HexStringToBytes(txtKey.Text).Length;
				KeySizes keysize = mSelectedAlgorithm.LegalKeySizes[0];
				if (keylength == keysize.MaxSize / 8)
				{
					txtKey.BackColor = Color.White;
					mErrorProvider.SetError(txtKey, "");
					tlpSenderMetadata.SetColumnSpan(txtKey, 2);
					return true;
				}
			}
			// These exceptions occur when the user types in strings that cannot
			// be parsed into bytes
			catch (ArgumentOutOfRangeException) { }
			catch (FormatException) { }

			txtKey.BackColor = Color.Pink;
			tlpSenderMetadata.SetColumnSpan(txtKey, 1);
			mErrorProvider.SetError(txtKey,
				string.Format(
				@"The {0} algorithm requires a key {1} bytes.",
				TypeHelper.GetFriendlyName(mSelectedAlgorithm.GetType()),
				mSelectedAlgorithm.LegalKeySizes[0].MaxSize / 4)
			);
			return false;
		}

		private bool ValidateIV()
		{
			try
			{
				int ivlength = ConvertHelper.HexStringToBytes(txtIV.Text).Length;
				if (mSelectedAlgorithm.BlockSize / 8 == ivlength)
				{
					txtIV.BackColor = Color.White;
					tlpSenderMetadata.SetColumnSpan(txtIV, 2);
					mErrorProvider.SetError(txtIV, "");
					return true;
				}
			}
			// These exceptions occur when the user types in strings that cannot
			// be parsed into bytes
			catch (FormatException) { }
			catch (ArgumentOutOfRangeException) { }
			txtIV.BackColor = Color.Pink;
			tlpSenderMetadata.SetColumnSpan(txtIV, 1);
			mErrorProvider.SetError(txtIV,
				string.Format(
@"The {0} algorithm requires an
initialization vector of {1} bytes.",
				TypeHelper.GetFriendlyName(mSelectedAlgorithm.GetType()),
				mSelectedAlgorithm.BlockSize / 4)
			);
			return false;
		}

		#endregion Private Methods

	}
}
